/*
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

#include "SerialReader.h"


#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <iostream>
#include <memory.h>
#include <string>       // string function definitions


namespace Evil {

bool SerialReader::open( ConnectionInfo &info )
{
    m_conConf = info;
    m_usb = open( m_conConf.device(), O_RDWR| O_NOCTTY );
    struct termios tty;
    memset (&tty, 0, sizeof tty);

    /* Error Handling */
    if ( tcgetattr ( m_usb, &tty ) != 0 ) {
        std::cerr << "Error " 
                  << errno 
                  << " from tcgetattr: "
                  << strerror(errno) 
                  << std::endl;
        return false;
    }

    /* Save old tty parameters */
    m_oldTty = tty;

    /* Set Baud Rate */
    cfsetospeed ( &tty, static_cast< speed_t >( info.baudRate() ));
    cfsetispeed ( &tty, static_cast< speed_t >( info.baudRate() ));

    /* Setting other Port Stuff */
    tty.c_cflag         &=  ~PARENB;           // Make 8n1
    tty.c_cflag         &=  ~CSTOPB;
    tty.c_cflag         &=  ~CSIZE;
    tty.c_cflag         |=  CS8;
    tty.c_cflag         &=  ~CRTSCTS;           // no flow control
    tty.c_cflag         |=  CREAD | CLOCAL;     // turn on READ & ignore ctrlln
    tty.c_cc[ VMIN  ]    =  1;                  // read doesn't block
    tty.c_cc[ VTIME ]    =  10;                 // 0.5 seconds read timeout
                          

    /* Make raw */
    cfmakeraw( &tty );

    /* Flush Port, then applies attributes */
    tcflush( m_usb, TCIFLUSH );
    if ( tcsetattr ( m_usb, TCSANOW, &tty ) != 0) {
        std::cout << "Error " 
                  << errno 
                  << " from tcsetattr" 
                  << std::endl;
        return false;
    }
    fcntl( m_usb, F_SETFL, O_NONBLOCK );
    return true;
}


void SerialReader::write( const char *cmd, int len )
{
    int numWritten = 0;
    do {
        numWritten += ::write( m_usb, &cmd[ numWritten ], 1 );
    }
    while ( numWritten < len
            && cmd[ numWritten - 1 ] != '\r'
            && cmd[ numWritten - 1 ] != '\n'
            && numWritten > 0 );
    return numWritten != 0;
}


void SerialReader::write( std::string &msg )
{
    this->write( msg.c_str(), msg.size() );
}


std::string SerialReader::read()
{
    int n = 0;
    char buf;
    std::string response;
    int numTries = 1000;
    do {
        n = ::read( m_usb, &buf, 1 );
        response.append( &buf, 1 );
        usleep( 5 );
    } while( numTries-- && buf != '\r' );

    if (numTries == 0) {
        return "Error";
    }
    else if (n == 0) {
        std::cout << "Read nothing!" << std::endl;
    }
    return response;
}


void SerialReader::close()
{
    /* Flush Port, then applies attributes */
   tcflush( m_usb, TCIFLUSH );
   if ( tcsetattr ( m_usb, TCSANOW, &m_oldTty) != 0) {
       std::cout << "Error " 
                 << errno 
                 << " from tcsetattr while restting" 
                 << std::endl;
   }
   std::cout << "Closing device" << std::endl;
   fcntl( m_usb, F_SETFL, ~O_NONBLOCK );
   ::close( m_usb );
}

}